#include "bank.h"
#include <math.h>

using namespace std;
Bank::Bank()
{
    int hour;
    cout << "总模拟时间(小时):";
    cin >> hour;
    workTime = hour * 60 * 60;
    cout << "银行窗口数:";
    cin >> windowNums;
    cout << "平均每小时顾客数量:";
    cin >> ArriNums;
    cout << "单个窗口平均每小时服务客户数量:";
    cin >> ability;
    cout << "是否要展示模拟过程?(yes/no):";
    string answer;
    while (true)
    {
        cin >> answer;
        if (answer == "yes" || answer == "y" || answer == "Y" || answer == "Yes")
        {
            ifShowProcess = true;
            break;
        }
        else if (answer == "no" || answer == "n" || answer == "N" || answer == "No")
        {
            ifShowProcess = false;
            break;
        }
        else
        {
            cout << "输入错误，请输入yes or no!";
        }
        cin.clear(); //防止用户输入了EOF或其他方式导致cin被置为false;
        cin.sync();  //清空缓存，防止用户回车前输入了多个单词;
    }
}

void Bank::simulation(int rounds)
{
    int currCus = 0;
    int lastCus = 0;
    for (; currTime <= workTime; currTime++)
    {

        if (ifCustomerArrive() && waitingQueue.size() < 1000)
        {
            /*设置等候队列上限，如果队列太长，则禁止顾客进入，但是
            这样势必会增加后面时间段中，顾客到来的概率增大，此模型未考虑这一因素*/
            waitingQueue.push(Customer(currTime));
        }
        if (ifWindowsIdly() && !waitingQueue.empty())
        {
            /*若等候队列为空，说明此刻供不应求，出现这种情况肯定会影响
            窗口空闲概率分布，例如：长时间的供不应求情况出现时候，势
            必会导致很多窗口闲置，势必会造成后面时间段中窗口闲置的概率
            增加，此模型未考虑这一因素*/
            servedQueue.push_back(
                waitingQueue.front().set_getServeTime(currTime).set_WaitingTime());
            waitingQueue.pop();
        }
        if (false != ifShowProcess)
        {
            // 如果有客户到来,则输出当前状态信息
            currCus = waitingQueue.size() + servedQueue.size();
            if (currCus > lastCus)
            {
                cout << "又有新顾客来了" << endl;
                cout << "当前时间："
                     << RealTime(currTime).setStartTime(RealTime(8, 0, 0));
                cout << "当前到店客户数:" << waitingQueue.size() + servedQueue.size() << endl;
                cout << "当前排队人数:" << waitingQueue.size() << endl;
                cout << "完成服务人数:" << servedQueue.size() << endl;
                cout << "\n";
                lastCus = currCus;
            }
        }
    }
}

bool Bank::ifCustomerArrive() const
{
    // 设置概率模型
    //平均每小时(3600秒)到达客户数
    int num = 12;
    /*”计算当前这1秒，有客户来的概率，并放大3600倍,即
    probability=概率*3600=num/(1*60*60)*3600=num
    因为这个概率算下来非常小,即使用double来存储，计算机也会四舍五入成0。
    所以必须放大，放大系数设置成3600，是为了方便。
    本模型认为1秒钟已经是非常短的时间，故认为同时到来多个客户的情况可以忽略，这也是模型缺陷*/
    double probability = num;

    // 建立随机试验
    //在[0,3600]之间随意以均匀概率取一个点(随机采样)
    double random = ((double)rand() / (RAND_MAX)) * 3600;

    // 判断随机试验结果并返回
    if (random <= probability) //若随机采样点位于[0,probability]区间，则认为有顾客到来
    {
        // cout<<"========================================================================================"<<endl;
        return true;
    }
    else //若随机采样点位于[probability,1]区间,则认为没有顾客到来。
        return false;
}
bool Bank::ifWindowsIdly() const
{
    // 设置概率模型
    int ablity = 10;                   //单个窗口服务能力---平均每小时(3600秒)服务客户数。
    int ablitys = ablity * windowNums; //所有窗口累计服务能力。
    /*计算当前这1秒，银行所有窗口能够输出1个服务能力的概率，并放大3600倍。
    即probability=概率*3600=ablitys/(1*60*60)*3600=ablitys
    因为这个概率算下来非常小,即使用double来存储，计算机也会四舍五入成0。
    所以必须放大，放大系数设置成3600，是为了方便
    本模型认为1秒钟已经是非常短的时间，故认为同时可以输出多个服务能力(同时多个窗口空闲)的情况可以忽略，这也是模型缺陷
    */
    double probability = ablitys;

    //建立随机试验
    double random = ((double)rand() / (RAND_MAX)) * 3600; //在[0,3600]之间随意以均匀概率采样(随机采样)

    // 判断随机试验结果并返回
    if (random <= (probability)) //若随机采样点位于[0，probability]区间，则认为有顾客到来
        return true;
    else //若随机采样点位于[probability,1]区间,则认为没有顾客到来。
        return false;
}

ostream &operator<<(ostream &out, Bank &bank)
{

    if (bank.currTime < (bank.workTime - 1))
    {
        out << "please simulation!" << endl;
        return out;
    }
    bank.sumCusServed = bank.servedQueue.size();
    bank.lastCus = bank.waitingQueue.size();
    bank.sumCusArrive = bank.sumCusServed + bank.lastCus;
    for (const auto &x : bank.servedQueue)
    {
        bank.sumWaitingTime += x.get_WaitingTime();
    }
    bank.aver_waitingTime = bank.sumWaitingTime / bank.sumCusServed;
    bank.per_second_servedCus = bank.sumCusServed / bank.workTime;

    out << "————————————————模拟结果————————————————" << endl;
    out << "总模拟:" << RealTime(bank.workTime);
    out << "窗口:" << bank.windowNums << "个" << endl;
    out << "总客户人数:" << bank.sumCusArrive << "人" << endl;
    out << "   已服务:" << bank.sumCusServed << "人" << endl;
    out << "   未服务:" << bank.waitingQueue.size() << "人" << endl;
    out << "累计等候时间:" << RealTime(bank.sumWaitingTime);
    out << "    平均到达:" << bank.sumCusArrive / (bank.workTime / 3600) << "人/小时" << endl;
    out << "平均等候(/人):" << RealTime(bank.aver_waitingTime);
    out << "    服务能力:" << bank.per_second_servedCus * 3600 << "人/小时" << endl;
    return out;
};
